package heap

import (
	"archive/zip"
	"bytes"
	"io/ioutil"
	"strings"
	"sync"

	"github.com/allegro/bigcache"
	"github.com/golang/protobuf/proto"
	"hundsun.com/hsl/hschain/common/log/logging"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/classfile"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/classpath"
	jvmComm "hundsun.com/hsl/hschain/contract/jvm/jvm.go/common"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/vm"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/vmutils"
	"hundsun.com/hsl/hschain/contract/jvm/types"
	execCommon "hundsun.com/hsl/hschain/executor/common"
	"hundsun.com/hsl/hschain/protos/common"
	"hundsun.com/hsl/hschain/store/db"
	"hundsun.com/hsl/hschain/store/extend/contract"
)

const (
	jlObjectClassName       = "java/lang/Object"
	jlClassClassName        = "java/lang/Class"
	jlStringClassName       = "java/lang/String"
	jlThreadClassName       = "java/lang/Thread"
	jlCloneableClassName    = "java/lang/Cloneable"
	ioSerializableClassName = "java/io/Serializable"
)

var BaseContractClasses = [...]string{
	"java/io/ObjectStreamField",
	"java/lang/System",
	"java/io/ObjectStreamField",
	"java/util/Comparator",
	"java/lang/String$CaseInsensitiveComparator",
	"java/lang/Thread$UncaughtExceptionHandler",
	"java/lang/ThreadGroup",
	"java/lang/AutoCloseable",
	"java/io/Closeable",
	"java/io/Flushable",
	"java/io/OutputStream",
	"java/io/FilterOutputStream",
	"java/lang/Appendable",
	"java/io/PrintStream",
	"java/lang/System",
	"java/security/AccessController",
	"java/security/AccessControlContext",
	"java/util/Dictionary",
	"java/util/Map",
	"java/util/Hashtable",
	"java/util/Properties",
	"java/lang/Number",
	"java/lang/Float",
	"java/util/Map$Entry",
	"java/util/Hashtable$Entry",
	"java/lang/Math",
	"java/lang/StackTraceElement",
	"java/lang/Double",
	"java/security/Guard",
	"java/security/Permission",
	"java/security/BasicPermission",
	"java/lang/RuntimePermission",
	"sun/misc/VM",
	"sun/reflect/misc/ReflectUtil",
	"java/util/concurrent/ConcurrentHashMap$CollectionView",
	"java/util/concurrent/ConcurrentHashMap$KeySetView",
	"java/util/concurrent/ConcurrentHashMap$ValuesView",
	"java/util/concurrent/ConcurrentHashMap$EntrySetView",
	"java/util/concurrent/ConcurrentHashMap$CounterCell",
	"sun/util/locale/BaseLocale",
	"sun/util/locale/BaseLocale$Cache",
	"sun/util/locale/BaseLocale$Key",
	"sun/util/locale/LocaleObjectCache$CacheEntry",
	"java/util/concurrent/ConcurrentHashMap$Node",
	"java/util/Locale$LocaleKey",
	"sun/util/locale/LocaleUtils",
	"sun/security/action/GetPropertyAction",
	"java/lang/Character",
	"java/lang/CharacterData",
	"java/lang/CharacterDataLatin1",
	"java/lang/Integer$IntegerCache",
	"java/io/BufferedOutputStream",
	"java/nio/charset/Charset",
	"java/nio/charset/spi/CharsetProvider",
	"sun/nio/cs/FastCharsetProvider",
	"sun/nio/cs/StandardCharsets",
	"sun/util/PreHashedMap",
	"sun/nio/cs/StandardCharsets$Aliases",
	"sun/nio/cs/StandardCharsets$Classes",
	"sun/nio/cs/StandardCharsets$Cache",
	"java/lang/ThreadLocal",
	"java/util/concurrent/atomic/AtomicInteger",
	"java/nio/charset/Charset",
	"sun/nio/cs/HistoricallyNamedCharset",
	"sun/nio/cs/Unicode",
	"sun/nio/cs/UTF_8",
	"java/nio/charset/spi/CharsetProvider",
	"sun/nio/cs/FastCharsetProvider",
	"sun/nio/cs/StandardCharsets",
	"sun/util/PreHashedMap",
	"sun/nio/cs/StandardCharsets$Aliases",
	"sun/nio/cs/StandardCharsets$Classes",
	"java/util/SortedSet",
	"java/util/NavigableSet",
	"java/util/TreeSet",
	"sun/nio/cs/StandardCharsets$Cache",
	"java/util/LinkedHashMap",
	"java/lang/ThreadLocal",
	"java/util/concurrent/atomic/AtomicInteger",
	"java/lang/Class$1",
	"sun/reflect/ReflectionFactory$1",
	"java/util/SortedMap",
	"java/util/NavigableMap",
	"java/util/TreeMap",
	"sun/reflect/MagicAccessorImpl",
	"sun/reflect/ConstructorAccessor",
	"sun/reflect/ConstructorAccessorImpl",
	"sun/reflect/NativeConstructorAccessorImpl",
	"sun/reflect/DelegatingConstructorAccessorImpl",
	"org/apache/commons/lang3/tuple/Pair",
	"java/lang/ClassCastException",
	"java/lang/Byte",
	"java/lang/ReflectiveOperationException",
	"java/lang/ClassNotFoundException",
	"java/lang/Short",
	"java/lang/IllegalAccessException",
	"java/lang/InstantiationException",
	"java/lang/Long",
	"java/lang/IllegalArgumentException",
	"java/nio/charset/IllegalCharsetNameException",
	"java/nio/charset/UnsupportedCharsetException",
	"java/io/IOException",
	"java/io/UnsupportedEncodingException",
	"java/math/BigInteger",
	"java/io/ByteArrayInputStream",
	"java/io/DataInput",
	"java/io/ObjectInput",
	"java/io/ObjectStreamConstants",
	"java/io/ObjectInputStream",
	"java/lang/Void",
	"java/io/ObjectInputStream$BlockDataInputStream",
	"java/io/ObjectInputStream$PeekInputStream",
	"java/io/DataInputStream",
	"java/io/ObjectInputStream$HandleTable",
	"java/io/ObjectInputStream$HandleTable$HandleList",
	"java/io/ObjectInputStream$ValidationList",
	"java/io/Bits",
	"org/apache/commons/lang3/ClassUtils",
	"java/util/HashMap$EntrySet",
	"java/util/HashMap$HashIterator",
	"java/util/HashMap$EntryIterator",
	"java/util/Collections$UnmodifiableMap",
	"org/apache/commons/lang3/StringUtils",
	"org/apache/commons/lang3/Validate",
	"java/lang/StrictMath",
	"java/util/Set",
	"java/util/AbstractSet",
	"java/util/Hashtable$EntrySet",
	"java/util/Locale",
	"java/util/Collections",
	"sun/util/locale/LocaleObjectCache",
	"java/util/Locale$Cache",
	"java/util/Collections$EmptySet",
	"java/lang/ref/ReferenceQueue",
	"java/lang/ref/ReferenceQueue$Null",
	"java/util/Collections$EmptyList",
	"java/lang/ref/ReferenceQueue$Lock",
	"java/util/AbstractMap",
	"java/util/Collections$EmptyMap",
	"java/util/Collections$SynchronizedCollection",
	"java/util/Collections$SynchronizedSet",
	"java/util/Objects",
	"java/util/Enumeration",
	"java/util/Iterator",
	"java/util/Hashtable$Enumerator",
	"sun/misc/Version",
	"java/util/Map",
	"java/util/AbstractMap",
	"java/util/concurrent/ConcurrentMap",
	"java/io/InputStream",
	"java/io/FileInputStream",
	"java/util/concurrent/ConcurrentHashMap",
	"java/io/FileDescriptor",
	"java/lang/Runtime",
	"sun/misc/JavaIOFileDescriptorAccess",
	"java/io/FileDescriptor$1",
	"sun/misc/SharedSecrets",
	"sun/misc/Unsafe",
	"java/lang/Appendable",
	"java/lang/AbstractStringBuilder",
	"sun/reflect/Reflection",
	"java/lang/StringBuilder",
	"java/util/concurrent/locks/Lock",
	"java/util/concurrent/locks/ReentrantLock",
	"java/util/concurrent/ConcurrentHashMap$Segment",
	"java/util/HashMap",
	"java/util/HashMap$Node",
	"java/io/FileOutputStream",
	"java/io/FilterInputStream",
	"java/io/BufferedInputStream",
	"java/util/concurrent/atomic/AtomicReferenceFieldUpdater",
	"java/util/Arrays",
	"java/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl",
	"java/security/PrivilegedExceptionAction",
	"java/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl$1",
	"java/lang/Math",
	"java/security/AccessController",
	"java/lang/Number",
	"java/lang/Float",
	"sun/reflect/Reflection",
	"java/lang/Double",
	"java/util/HashMap",
	"java/lang/Integer",
	"java/util/Map$Entry",
	"java/util/HashMap$Node",
	"java/lang/Class$3",
	"sun/misc/Unsafe",
	"java/lang/Class$ReflectionData",
	"java/lang/ref/Reference",
	"java/lang/ref/SoftReference",
	"sun/misc/VM",
	"java/lang/ref/Reference$Lock",
	"java/lang/ref/Reference$ReferenceHandler",
	"java/lang/Class$Atomic",
	"java/util/Dictionary",
	"java/util/Properties",
	"java/lang/reflect/AccessibleObject",
	"java/lang/reflect/Member",
	"java/util/Hashtable$Entry",
	"java/lang/reflect/Field",
	"java/util/LinkedHashMap$Entry",
	"java/util/HashMap$TreeNode",
	"java/lang/NullPointerException",
	"java/util/Collections$UnmodifiableCollection",
	"java/util/Collections$UnmodifiableList",
	"java/util/Collections$UnmodifiableRandomAccessList",
	"java/lang/reflect/Executable",
	"java/lang/reflect/Constructor",
	"java/security/PrivilegedActionException",
	"java/lang/Throwable$PrintStreamOrWriter",
	"java/lang/Throwable$WrappedPrintStream",
	"java/util/IdentityHashMap",
	"java/lang/ClassLoader",
	"java/security/ProtectionDomain",
	"java/util/Collections$SetFromMap",
	"java/util/IdentityHashMap$KeySet",
	"sun/reflect/generics/repository/AbstractRepository",
	"sun/reflect/generics/repository/GenericDeclRepository",
	"sun/reflect/generics/repository/ClassRepository",
	"java/lang/Boolean",
	"sun/reflect/ReflectionFactory",
	"java/lang/Class$AnnotationData",
	"sun/reflect/annotation/AnnotationType",
	"java/util/WeakHashMap",
	"java/lang/ClassValue$ClassValueMap",
	"sun/reflect/ReflectionFactory$GetReflectionFactoryAction",
	"java/lang/reflect/Modifier",
	"sun/reflect/LangReflectAccess",
	"java/lang/reflect/ReflectAccess",
	"java/lang/reflect/ReflectPermission",
	"java/lang/NullPointerException",
	"java/lang/Error",
	"sun/reflect/MethodAccessor",
	"sun/reflect/MethodAccessorImpl",
	"sun/reflect/NativeMethodAccessorImpl",
	"sun/reflect/DelegatingMethodAccessorImpl",
	"sun/reflect/annotation/ExceptionProxy",
	"sun/reflect/annotation/AnnotationTypeMismatchExceptionProxy",
	"sun/reflect/annotation/AnnotationParser$1",
	"java/lang/reflect/InvocationHandler",
	"sun/reflect/annotation/AnnotationInvocationHandler",
	"java/lang/reflect/Proxy",
	"java/lang/reflect/WeakCache",
	"java/util/function/BiFunction",
	"java/lang/reflect/Proxy$KeyFactory",
	"java/lang/reflect/Proxy$ProxyClassFactory",
	"java/util/concurrent/atomic/AtomicLong",
	"java/lang/ref/WeakReference",
	"java/lang/reflect/WeakCache$CacheKey",
	"java/lang/reflect/Proxy$Key1",
	"java/util/function/Supplier",
	"java/lang/reflect/WeakCache$Factory",
	"sun/misc/ProxyGenerator",
	"sun/security/action/GetBooleanAction",
	"java/lang/InterruptedException",
	"sun/misc/ProxyGenerator$ConstantPool",
	"sun/misc/ProxyGenerator$ProxyMethod",
	"java/lang/reflect/Array",
	"java/util/HashMap$Values",
	"java/util/HashMap$ValueIterator",
	"sun/misc/ProxyGenerator$MethodInfo",
	"java/io/ByteArrayOutputStream",
	"sun/misc/ProxyGenerator$ConstantPool$Entry",
	"sun/misc/ProxyGenerator$ConstantPool$ValueEntry",
	"java/io/DataOutput",
	"java/io/DataOutputStream",
	"sun/misc/ProxyGenerator$ConstantPool$IndirectEntry",
	"sun/misc/ProxyGenerator$FieldInfo",
	"sun/misc/ProxyGenerator$PrimitiveTypeInfo",
	"sun/misc/ProxyGenerator$ExceptionTableEntry",
	"java/lang/reflect/WeakCache$Value",
	"java/lang/reflect/WeakCache$CacheValue",
	"java/io/Writer",
	"java/io/OutputStreamWriter",
	"sun/nio/cs/StreamEncoder",
	"java/nio/charset/CharsetEncoder",
	"sun/nio/cs/ArrayEncoder",
	"sun/nio/cs/UTF_8$Encoder",
	"java/nio/charset/CodingErrorAction",
	"java/nio/Buffer",
	"java/nio/ByteBuffer",
	"java/nio/HeapByteBuffer",
	"java/nio/Bits",
	"java/nio/ByteOrder",
	"sun/misc/JavaNioAccess",
	"java/nio/Bits$1",
	"java/io/BufferedWriter",
	"java/io/File",
	"java/io/DefaultFileSystem",
	"java/io/FileSystem",
	"java/io/WinNTFileSystem",
	"java/io/ExpiringCache",
	"java/io/ExpiringCache$1",
	"java/lang/Enum",
	"java/io/File$PathStatus",
	"java/nio/file/Watchable",
	"java/nio/file/Path",
	"java/security/cert/Certificate",
	"java/util/Vector",
	"java/lang/Class$MethodArray",
	"java/util/Vector",
	"java/util/Stack",
	"java/lang/Terminator",
	"java/lang/reflect/Method",
	"sun/misc/SignalHandler",
	"java/lang/Terminator$1",
	"java/io/ObjectOutput",
	"java/io/ObjectOutputStream",
	"java/io/ObjectOutputStream$BlockDataOutputStream",
	"java/io/ObjectOutputStream$HandleTable",
	"java/io/ObjectOutputStream$ReplaceTable",
	"java/io/ObjectStreamClass",
	"java/io/ObjectStreamClass$Caches",
	"java/io/ObjectStreamClass$WeakClassKey",
	"java/io/ObjectStreamClass$EntryFuture",
	"java/io/Externalizable",
	"java/io/ObjectStreamClass$2",
	"sun/reflect/UnsafeFieldAccessorFactory",
	"sun/reflect/FieldAccessor",
	"sun/reflect/FieldAccessorImpl",
	"sun/reflect/UnsafeFieldAccessorImpl",
	"sun/reflect/UnsafeStaticFieldAccessorImpl",
	"sun/reflect/UnsafeQualifiedStaticFieldAccessorImpl",
	"sun/reflect/UnsafeQualifiedStaticLongFieldAccessorImpl",
	"sun/reflect/UnsafeQualifiedStaticObjectFieldAccessorImpl",
	"java/util/Arrays$LegacyMergeSort",
	"java/util/ComparableTimSort",
	"sun/reflect/ClassFileConstants",
	"sun/reflect/AccessorGenerator",
	"sun/reflect/MethodAccessorGenerator",
	"sun/reflect/ByteVectorFactory",
	"sun/reflect/ByteVector",
	"sun/reflect/ByteVectorImpl",
	"sun/reflect/ClassFileAssembler",
	"sun/reflect/UTF8",
	"java/lang/StringBuffer",
	"sun/reflect/Label",
	"sun/reflect/Label$PatchInfo",
	"sun/reflect/MethodAccessorGenerator$1",
	"sun/reflect/ClassDefiner",
	"sun/reflect/ClassDefiner$1",
	"sun/reflect/DelegatingClassLoader",
	"sun/misc/JavaSecurityAccess",
	"java/security/ProtectionDomain$JavaSecurityAccessImpl",
	"sun/security/util/Debug",
	"sun/misc/JavaSecurityProtectionDomainAccess",
	"java/security/ProtectionDomain$2",
	"java/security/CodeSource",
	"java/security/ProtectionDomain$Key",
	"java/security/Principal",
	"java/lang/ClassLoader$ParallelLoaders",
	"java/util/WeakHashMap$Entry",
	"java/util/WeakHashMap$KeySet",
	"java/util/HashSet",
	"sun/reflect/SerializationConstructorAccessorImpl",
	"sun/reflect/BootstrapConstructorAccessorImpl",
	"java/lang/NoSuchMethodException",
	"java/io/ObjectStreamClass$FieldReflectorKey",
	"java/io/ObjectStreamClass$FieldReflector",
	"sun/misc/Signal",
	"sun/misc/NativeSignalHandler",
	"sun/misc/OSEnvironment",
	"sun/io/Win32ErrorMode",
	"java/lang/Readable",
	"java/nio/CharBuffer",
	"java/nio/HeapCharBuffer",
	"java/nio/charset/CoderResult",
	"java/nio/charset/CoderResult$Cache",
	"java/nio/charset/CoderResult$1",
	"java/nio/charset/CoderResult$2",
	"sun/reflect/ConstantPool",
	"sun/misc/JavaLangAccess",
	"java/lang/System$2",
	"sun/misc/JavaLangAccess",
	"java/lang/System$2",
	"sun/reflect/annotation/AnnotationParser",
	"sun/reflect/generics/parser/SignatureParser",
	"sun/reflect/generics/tree/Tree",
	"sun/reflect/generics/tree/TypeTree",
	"sun/reflect/generics/tree/TypeArgument",
	"sun/reflect/generics/tree/ReturnType",
	"sun/reflect/generics/tree/TypeSignature",
	"sun/reflect/generics/tree/BaseType",
	"sun/reflect/generics/tree/FieldTypeSignature",
	"sun/reflect/generics/tree/SimpleClassTypeSignature",
	"sun/reflect/generics/tree/ClassTypeSignature",
	"sun/reflect/generics/scope/Scope",
	"sun/reflect/generics/scope/AbstractScope",
	"sun/reflect/generics/scope/ClassScope",
	"sun/reflect/generics/factory/GenericsFactory",
	"sun/reflect/generics/factory/CoreReflectionFactory",
	"sun/reflect/generics/visitor/TypeTreeVisitor",
	"sun/reflect/generics/visitor/Reifier",
	"java/lang/reflect/GenericArrayType",
	"sun/reflect/annotation/AnnotationType$1",
	"java/lang/annotation/Retention",
	"java/lang/annotation/Inherited",
	"java/lang/annotation/Target",
	"java/lang/annotation/RetentionPolicy",
	"java/lang/Class$4",
	"java/lang/LinkageError",
	"java/lang/IncompatibleClassChangeError",
	"java/lang/NoSuchMethodError",
	"java/nio/BufferUnderflowException",
	"java/util/ArrayList$Itr",
	"com/hundsun/hsl2/basecontract/ContractChecker/AuthContract",
	"com/hundsun/hsl2/basecontract/ContractChecker/ConstractClassChecker",
	"com/hundsun/hsl2/basecontract/ContractChecker/ContractTemplate",
	"com/hundsun/hsl2/basecontract/ContractChecker/DeployedContract",
	"com/hundsun/hsl2/basecontract/ContractChecker/JavaStateMachineHelper",
	"com/hundsun/hsl2/basecontract/ContractChecker/MethodChecker",
	"com/hundsun/hsl2/basecontract/annotation/Indexed",
	"com/hundsun/hsl2/basecontract/annotation/JavaEventFunction",
	"com/hundsun/hsl2/basecontract/annotation/NettySerialization",
	"com/hundsun/hsl2/basecontract/annotation/Pure",
	"com/hundsun/hsl2/basecontract/annotation/Storage",
	"com/hundsun/hsl2/basecontract/bcp/DecoderException",
	"com/hundsun/hsl2/basecontract/bcp/Encoder",
	"com/hundsun/hsl2/basecontract/bcp/EncoderException",
	"com/hundsun/hsl2/basecontract/bcp/Hex",
	"com/hundsun/hsl2/basecontract/bcp/HexEncoder",
	"com/hundsun/hsl2/basecontract/bcp/Strings$1",
	"com/hundsun/hsl2/basecontract/bcp/Strings",
	"com/hundsun/hsl2/basecontract/dao/ESDao",
	"com/hundsun/hsl2/basecontract/dao/EventDao",
	"com/hundsun/hsl2/basecontract/dao/TransferDao",
	"com/hundsun/hsl2/basecontract/dao/MainDao",
	"com/hundsun/hsl2/basecontract/dao/TxDao",
	"com/hundsun/hsl2/basecontract/type/CommonParam",
	"com/hundsun/hsl2/basecontract/type/EventParameter",
	"com/hundsun/hsl2/basecontract/type/JavaEvent$1",
	"com/hundsun/hsl2/basecontract/type/JavaEvent$ContractEventBuilder",
	"com/hundsun/hsl2/basecontract/type/JavaEvent$SDKEventBuilder",
	"com/hundsun/hsl2/basecontract/type/JavaEvent",
	"com/hundsun/hsl2/basecontract/type/JavaFunction",
	"com/hundsun/hsl2/basecontract/type/JavaType",
	"com/hundsun/hsl2/basecontract/util/BytesUtil",
	"com/hundsun/hsl2/basecontract/util/FileUtils",
	"com/hundsun/hsl2/basecontract/util/HexUtils",
	"com/hundsun/hsl2/basecontract/util/ParameterCheckUtils",
}

/*
class names:
    - primitive types: boolean, byte, int ...
    - primitive arrays: [Z, [B, [I ...
    - non-array classes: java/lang/Object ...
    - array classes: [Ljava/lang/Object; ...
*/

// the bootstrap class loader
type ClassLoader struct {
	rt        *Runtime
	classPath *classpath.ClassPath
	Cache     *bigcache.BigCache
	classMap  map[string]*Class // loaded classes
	verbose   bool
	// some frequently used classes
	jlObjectClass       *Class
	jlClassClass        *Class
	jlStringClass       *Class
	jlThreadClass       *Class
	jlCloneableClass    *Class
	ioSerializableClass *Class
	baseCode            *[]byte
	ContractAddress     string
	classMapLock        sync.RWMutex
	Log                 logging.Log
	ExecCtx             *execCommon.ExecContent
}

func IsJdkClass(name string) bool {
	jdkClasses := []string{"java/", "javax/", "com/sun", "sun/", "oracle/", "com/oracle/", "jdk/"}
	for _, j := range jdkClasses {
		if strings.HasPrefix(name, j) {
			return true
		}
	}

	if isPrimitiveType(name) || isPrimitiveArrayType(name) || strings.HasPrefix(name, "[Ljava") {
		return true
	}
	return false
}
func IsBaseContractClass(name string) bool {
	baseContractPrefix := []string{
		"org/apache/",
		"com/hundsun/hsl2/",
	}
	for _, p := range baseContractPrefix {
		if strings.HasPrefix(name, p) {
			return true
		}
	}
	return false
}
func NewBootloader(cp *classpath.ClassPath, db db.Database) *ClassLoader {
	bl := newBootLoader(cp, db, false)
	//bl.init()
	return bl
}

func newBootLoader(cp *classpath.ClassPath, db db.Database, verbose bool) *ClassLoader {
	//for test
	cl := &ClassLoader{
		classPath: cp,
		classMap:  map[string]*Class{},
		verbose:   verbose,
	}
	contractStore := contract.NewContractCodeDB(db)
	ct, errGet := contractStore.GetContractCode(types.BaseContractAddr)
	if errGet == nil && ct != nil && len(ct.Code) > 0 {
		cl.baseCode = &(ct.Code)
	}

	return cl
}

func (loader *ClassLoader) init() {
	loader.jlObjectClass = loader.LoadClass(jlObjectClassName)
	loader.jlClassClass = loader.LoadClass(jlClassClassName)
	for _, class := range loader.classMap {
		if class.JClass == nil {
			class.JClass = loader.jlClassClass.NewObj()
			class.JClass.Extra = class
		}
	}
	loader.jlCloneableClass = loader.LoadClass(jlCloneableClassName)
	loader.ioSerializableClass = loader.LoadClass(ioSerializableClassName)
	loader.jlThreadClass = loader.LoadClass(jlThreadClassName)
	loader.jlStringClass = loader.LoadClass(jlStringClassName)
	loader.loadPrimitiveClasses()
	loader.loadPrimitiveArrayClasses()
	//add by xuzt
	//load base contract class
	for _, baseName := range BaseContractClasses {
		loader.LoadClass(baseName)
	}
}
func (loader *ClassLoader) CopyBootloader(bl *ClassLoader) {
	loader.jlObjectClass = bl.jlObjectClass
	loader.jlObjectClass.bootLoader = loader
	loader.jlClassClass = bl.jlClassClass
	loader.jlClassClass.bootLoader = loader
	//loader.classMapLock.Lock()
	for name, class := range bl.classMap {
		if class != nil {
			class.bootLoader = loader
			loader.classMap[name] = class
		}
	}
	//loader.classMapLock.Unlock()
	loader.jlCloneableClass = bl.jlCloneableClass
	loader.jlCloneableClass.bootLoader = loader
	loader.ioSerializableClass = bl.ioSerializableClass
	loader.ioSerializableClass.bootLoader = loader
	loader.jlThreadClass = bl.jlThreadClass
	loader.jlThreadClass.bootLoader = loader
	loader.jlStringClass = bl.jlStringClass
	loader.jlStringClass.bootLoader = loader
	loader.baseCode = bl.baseCode
}
func (loader *ClassLoader) loadPrimitiveClasses() {
	for _, primitiveType := range primitiveTypes {
		loader.loadPrimitiveClass(primitiveType.Name)
	}
}
func (loader *ClassLoader) loadPrimitiveClass(className string) {
	class := &Class{Name: className}
	class.bootLoader = loader
	class.JClass = loader.jlClassClass.NewObj()
	class.JClass.Extra = class
	class.MarkFullyInitialized()
	loader.classMapLock.Lock()
	loader.classMap[className] = class
	loader.classMapLock.Unlock()
}

func (loader *ClassLoader) loadPrimitiveArrayClasses() {
	for _, primitiveType := range primitiveTypes {
		loader.loadArrayClass(primitiveType.ArrayClassName)
	}
}
func (loader *ClassLoader) loadArrayClass(className string) *Class {
	class := &Class{Name: className}
	class.bootLoader = loader
	class.SuperClass = loader.jlObjectClass
	class.Interfaces = []*Class{loader.jlCloneableClass, loader.ioSerializableClass}
	class.JClass = loader.jlClassClass.NewObj()
	class.JClass.Extra = class
	createVtable(class)
	class.MarkFullyInitialized()
	loader.classMapLock.Lock()
	loader.classMap[className] = class
	loader.classMapLock.Unlock()
	return class
}

func (loader *ClassLoader) getRefArrayClass(componentClass *Class) *Class {
	arrClassName := "[L" + componentClass.Name + ";"
	return loader.getRefArrayClassByName(arrClassName)
}
func (loader *ClassLoader) getRefArrayClassByName(arrClassName string) *Class {
	loader.classMapLock.RLock()
	if arrClass, ok := loader.classMap[arrClassName]; ok {
		loader.classMapLock.RUnlock()
		return arrClass
	}
	loader.classMapLock.RUnlock()
	return loader.loadArrayClass(arrClassName)
}

func (loader *ClassLoader) JLObjectClass() *Class {
	return loader.jlObjectClass
}
func (loader *ClassLoader) JLClassClass() *Class {
	return loader.jlClassClass
}
func (loader *ClassLoader) JLStringClass() *Class {
	return loader.jlStringClass
}
func (loader *ClassLoader) JLThreadClass() *Class {
	return loader.jlThreadClass
}

// todo
func (loader *ClassLoader) GetPrimitiveClass(name string) *Class {
	return loader.getClass(name)
}

func (loader *ClassLoader) FindLoadedClass(name string) *Class {
	loader.classMapLock.RLock()
	defer loader.classMapLock.RUnlock()
	if class, ok := loader.classMap[name]; ok {
		return class
	}
	return nil
}

// todo dangerous
func (loader *ClassLoader) getClass(name string) *Class {
	loader.classMapLock.RLock()
	defer loader.classMapLock.RUnlock()
	if class, ok := loader.classMap[name]; ok {
		return class
	}
	panic("class not loaded! " + name)
}

func (loader *ClassLoader) LoadClass(name string) *Class {
	loader.classMapLock.RLock()
	//fmt.Printf("LoadClass class %s loader is %p classMap is %p\n",name,loader,loader.classMap)
	class, ok := loader.classMap[name]
	loader.classMapLock.RUnlock()
	if ok {
		// already loaded
		return class
	} else if name[0] == '[' {
		// array class
		return loader.getRefArrayClassByName(name)
	} else {
		return loader.reallyLoadClass(name)
	}
}

//clear class map
func (loader *ClassLoader) ClearContractClass() {
	//fmt.Println("LoadClass ",name)
	loader.classMapLock.Lock()
	defer loader.classMapLock.Unlock()
	//智能合约代码需要每次刷新classMap
	for className, _ := range loader.classMap {
		if IsJdkClass(className) || IsBaseContractClass(className) {
			continue
		}
		delete(loader.classMap, className)
	}

	//
	//for _, entry := range loader.classPath.GetEntries() {
	//	if zipEntry, ok := entry.(*classpath.ZipEntry); ok {
	//		if zipEntry != nil {
	//			zipEntry.Close()
	//		}
	//	}
	//}

	//fmt.Printf("After clear contract class map length is %d\n",len(loader.classMap))

}
func (loader *ClassLoader) reallyLoadClass(name string) *Class {
	data := loader.readClassData(name)
	class := loader.loadClass(name, data)
	//class.LoadedFrom = cpEntry

	/*if !IsBaseContractClass(name) && !IsJdkClass(name) {
		fmt.Printf("loader %p [Loaded %s from %s]\n", loader,name)
	}*/

	return class
}

func (loader *ClassLoader) readClassData(name string) []byte {
	log := loader.Log
	contractAddress := loader.ContractAddress
	if IsBaseContractClass(name) {
		contractAddress = types.BaseContractAddr
	}
	//java相关的类和base-contract从classpath获取
	if IsJdkClass(name) || IsBaseContractClass(name) {
		_, classData := loader.classPath.ReadClass(name)
		return classData
	}

	if contractAddress == "" {
		return nil
	}

	//用户合约
	if loader.ExecCtx == nil {
		return nil
	}
	var contractCode []byte
	var err error
	var ctBytes []byte
	var ct common.Contract
	//先从本地缓冲中查找合约代码
	if loader.Cache != nil {
		ctBytes, err = loader.Cache.Get(contractAddress)
		if err == nil && len(ctBytes) > 0 {
			err = proto.Unmarshal(ctBytes, &ct)
			if err == nil {
				contractCode = ct.Code
			} else {
				panic(err)
			}
		}
	}
	if err != nil || len(contractCode) == 0 {
		// 从db中获取
		sct := contract.NewContractCodeDB(loader.ExecCtx)
		userCt, err := sct.GetContractCode(contractAddress)
		if err != nil {
			log.Errorf("get contract [%s] from ES failed: %s", contractAddress, err.Error())
			return nil
		}
		if userCt != nil {
			contractCode = userCt.Code
		}
		if loader.Cache != nil {
			bytes, err := proto.Marshal(userCt)
			if err == nil {
				loader.Cache.Set(contractAddress, bytes)
			}
		}

	}
	//从合约代码中读取class代码
	if contractCode != nil && len(contractCode) > 0 {
		classData, errRead := loader.ReadClassFromBytes(name, contractCode)
		if errRead != nil {
			return nil
		}
		if classData == nil {
			panic(vm.NewClassNotFoundError(vmutils.SlashToDot(name)))
		}

		return classData
	}
	return nil
}

//add by xuzt
func (loader *ClassLoader) ReadClassFromBytes(name string, ccode []byte) ([]byte, error) {
	log := loader.Log
	rd := bytes.NewReader(ccode)
	r, err := zip.NewReader(rd, int64(len(ccode)))
	if err != nil {
		return nil, err
	}
	code := make([]byte, 0)
	filename := name + ".class"
	for _, f := range r.File {
		if f.Name == filename {
			if rc, err := f.Open(); err != nil {
				log.Errorf("Contract file [%s] open failed,err:", filename, err)
				return nil, err
			} else {
				defer rc.Close()
				code, err = ioutil.ReadAll(rc)
				if err != nil {
					log.Errorf("read file [%s] failed!", filename, err)
					return nil, err
				}
				break
			}
		}
	}
	return code, nil
}
func (loader *ClassLoader) parseClassData(name string, data []byte) *Class {
	cf, err := classfile.Parse(data)
	if err != nil {
		// todo
		//panic("failed to parse class file: " + name + "! " + err.Error())
		return nil
	}

	return newClass(cf)
}

func (loader *ClassLoader) loadClass(name string, data []byte) *Class {
	class := loader.parseClassData(name, data)
	if class == nil {
		return nil
	}
	hackClass(class)
	loader.resolveSuperClass(class)
	loader.resolveInterfaces(class)
	calcStaticFieldSlotIds(class)
	calcInstanceFieldSlotIds(class)
	createVtable(class)
	prepare(class)
	// todo
	class.bootLoader = loader
	loader.classMapLock.Lock()
	//fmt.Printf("loadClass write classmap loader is %p,map is %p\n",loader,loader.classMap)
	loader.classMap[name] = class
	loader.classMapLock.Unlock()

	if loader.jlClassClass != nil {
		class.JClass = loader.jlClassClass.NewObj()
		class.JClass.Extra = class
	}

	return class
}

//add by xuzt
func (loader *ClassLoader) LoadClassForCheck(name string, data []byte) *Class {
	class := loader.parseClassData(name, data)
	hackClass(class)
	loader.resolveSuperClass(class)
	loader.resolveInterfaces(class)
	//	calcStaticFieldSlotIds(class)
	//	calcInstanceFieldSlotIds(class)
	//	createVtable(class)
	//	prepare(class)
	// todo

	return class
}

/*func (loader *ClassLoader)checkClassRecursiveDefine(class *Class,contractClasses*[]string)bool{
	for _,field := range class.Fields {
		for _,c := range *contractClasses {
			if field.Type().Name == c {
				if  !loader.checkClassRecursiveDefine(field.Type(),contractClasses) {
					return false
				}
			}else {

			}
		}
	}
	return true
}*/
func checkGraphLoop(v string, classGraph map[string][]string, visited map[string]int32) bool {
	//检测有向图中的环路
	visited[v] = 1
	cfs := classGraph[v]
	for _, cf := range cfs {
		if visited[cf] == 0 {
			if checkGraphLoop(cf, classGraph, visited) {
				return true
			}
		} else if visited[cf] == 1 {
			return true
		}
	}
	visited[v] = 2
	return false
}
func CheckIsDag(classGraph map[string][]string) bool {
	//检测有向图中的环路
	visited := make(map[string]int32)
	for v, _ := range classGraph {
		visited[v] = 0
		if checkGraphLoop(v, classGraph, visited) {
			return false
		}
	}

	return true
}
func (loader *ClassLoader) checkContractClass(class *Class, isMainClass bool) (bool, jvmComm.ChkCodeErrCode) {
	//1.检查主类是否是继承自BaseContract,并且其他类不能继承BaseContract
	if isMainClass {
		if class.superClassName != jvmComm.BaseContractClassName {
			return false, jvmComm.ChkCodeErrCodeMainClassNotExpendBaseContract
		}
	} else {
		if class.SuperClass.Name == jvmComm.BaseContractClassName {
			return false, jvmComm.ChkCodeErrCodeClassExpendBaseContract
		}
	}
	//2.检查是否引用了非状态机类,直接查看类常量池
	noStateMachineClasses := []string{
		"java/io",
		"java/nio",
		"java/util/concurrent",
		"java/net",
		"javax/net",
		"javax/xml/ws/spi/http",
		"java/time",
		"java/lang/reflect",
		"java/lang/Readable",
		"java/lang/Process",
		"java/lang/ProcessBuilder",
		"java/lang/ProcessBuilder$Redirect",
		"java/lang/Runtime",
		"java/lang/Thread$State",
		"java/lang/Thread",
		"java/lang/Runnable",
		"java/lang/ThreadGroup",
		"java/lang/ThreadLocal",
		"java/lang/Class",
		//"java/lang/System",
		"java/sql/Date",
		"java/sql/Time",
		"java/sql/Timestamp",
		"java/text/DateFormat",
		"java/text/DateFormatSymbols",
		"java/text/SimpleDateFormat",
		"java/util/Calendar",
		"java/util/Date",
		"java/util/GregorianCalendar",
		"java/util/SimpleTimeZone",
		"java/util/TimeZone",
		"java/util/spi/CalendarDataProvider",
		"java/util/spi/CalendarNameProvider",
		"java/util/spi/TimeZoneNameProvider",
		"java/lang/Object/hashCode",
		//java中的HashMap,ArrayList使用ESDao替换
	}

	cp := class.ConstantPool
	for _, notAllowedClass := range noStateMachineClasses {
		//fmt.Println(notAllowedClass)
		for i := 1; i < len(cp); i++ {
			//cons := cp.GetConstant(uint(i))
			cons := cp[i]
			var consName string
			if cons != nil {
				switch x := cons.(type) {
				case int32, float32:
				case int64, float64:
				case []byte: // utf8
					x = x
				case *ConstantString:
					consName = cp.GetConstantString(uint(i)).goStr
				case *ConstantClass:
					consName = cp.GetConstantClass(uint(i)).name
				case *ConstantFieldRef:
					consName = cp.GetConstantFieldRef(uint(i)).className
				case *ConstantMethodRef:
					consName = cp[i].(*ConstantMethodRef).className
				case *ConstantInterfaceMethodRef:
					consName = cp[i].(*ConstantInterfaceMethodRef).className
				case *ConstantInvokeDynamic:
					consName = cp[i].(*ConstantInvokeDynamic).name
				case *ConstantMethodHandle:
				case *ConstantMethodType:
				default:
					// todo
					//fmt.Printf("%T \n",x)
					// panic("todo")

				}
				//fmt.Printf("consName = %s,not allowed class is %s\n",consName,notAllowedClass)
				if strings.Contains(consName, notAllowedClass) || consName == notAllowedClass {
					return false, jvmComm.ChkCodeErrCodeUseNotAllowClass
				}

			}
		}
	}
	//3.检查method不能是synchronized,native
	for _, method := range class.Methods {
		if method.IsNative() {
			//fmt.Println("field is native type",method.Name)
			return false, jvmComm.ChkCodeErrCodeUseNative
		}
		if method.IsSynchronized() {
			//fmt.Println("field is Synchronized type",method.Name)
			return false, jvmComm.ChkCodeErrCodeUseSynchronized
		}
	}

	//5.ArrayList,HashMap等数据结构使用ESDao替代
	return true, jvmComm.ChkCodeErrCodeOk
}
func (loader *ClassLoader) GetMainContractClass(ccode []byte) string {
	rd := bytes.NewReader(ccode)
	r, err := zip.NewReader(rd, int64(len(ccode)))
	if err != nil {
		return ""
	}
	//先找到主类名
	for _, f := range r.File {
		suffixIndex := len(f.Name) - len(".class")
		if len(f.Name) <= len(".class") || f.Name[suffixIndex:] != ".class" {
			continue
		}
		className := f.Name[:suffixIndex]

		if strings.Contains(className, "Main") {
			//找到主类
			return className
		}
	}
	return ""
}
func (loader *ClassLoader) CheckContractClasses(ccode []byte) (bool, jvmComm.ChkCodeErrCode, string) {
	log := loader.Log
	rd := bytes.NewReader(ccode)
	r, err := zip.NewReader(rd, int64(len(ccode)))
	if err != nil {
		return false, jvmComm.ChkCodeErrCodeOther, ""
	}
	code := make([]byte, 0)
	mainClassName := ""
	contractClassName := ""
	var contractClasses []string
	//filename := name + ".class"
	//先找到主类名
	for _, f := range r.File {
		if strings.HasSuffix(f.Name, ".jar") {
			return false, jvmComm.ChkCodeErrCodeImportThirdJarPak, ""
		}
		suffixIndex := len(f.Name) - len(".class")
		if len(f.Name) <= len(".class") || f.Name[suffixIndex:] != ".class" {
			continue
		}
		className := f.Name[:suffixIndex]

		if strings.Contains(className, "Main") {
			//找到主类
			if len(className) <= 4 {
				return false, jvmComm.ChkCodeErrCodeOther, ""
			}
			mainClassName = className
			contractClassName = className[:len(className)-4]
			//break
		} else {
			contractClasses = append(contractClasses, className)
		}
	}
	if contractClassName == "" {
		log.Errorf("Contract class not found ")
		return false, jvmComm.ChkCodeErrCodeMainClassNotExist, ""
	}
	classMap := make(map[string]*Class)
	classGraph := make(map[string][]string)
	for _, f := range r.File {
		suffixIndex := len(f.Name) - len(".class")
		if len(f.Name) <= len(".class") || f.Name[suffixIndex:] != ".class" {
			continue
		}
		className := f.Name[:suffixIndex]
		if strings.Contains(className, "Main") {
			//Main类不需要Check
			continue
		}

		if rc, err := f.Open(); err != nil {
			log.Errorf("Class file %s open failed,err:%s", f.Name, err.Error())
			return false, jvmComm.ChkCodeErrCodeOther, ""
		} else {
			defer rc.Close()
			code, err = ioutil.ReadAll(rc)
			if err != nil {
				log.Errorf("Read class file %s failed %S!", f.Name, err.Error())
				return false, jvmComm.ChkCodeErrCodeOther, ""
			}

			class := loader.LoadClassForCheck(className, code)
			classMap[className] = class
		}
	}
	for ctClassName, ctClass := range classMap {
		for _, f := range ctClass.Fields {
			if len(f.Descriptor) > 1 && strings.HasPrefix(f.Descriptor, "L") {
				fieldClassName := f.Descriptor[1 : len(f.Descriptor)-1]
				if _, ok := classMap[fieldClassName]; ok {
					classGraph[ctClassName] = append(classGraph[ctClassName], fieldClassName)
				}
			}
		}
		isMainClass := false
		if ctClassName == contractClassName {
			isMainClass = true
		}
		checkPass, rst := loader.checkContractClass(ctClass, isMainClass)
		if !checkPass {
			return false, rst, ""
		}
	}

	//合约类不允许递归定义
	if !CheckIsDag(classGraph) {
		log.Errorf("Class recursive definition checked")
		return false, jvmComm.ChkCodeErrCodeClassRecursiveDefined, ""
	}
	return true, jvmComm.ChkCodeErrCodeOk, mainClassName
}

// todo
func hackClass(class *Class) {
	if class.Name == "java/lang/ClassLoader" {
		loadLibrary := class.GetStaticMethod("loadLibrary", "(Ljava/lang/Class;Ljava/lang/String;Z)V")
		loadLibrary.Code = []byte{0xb1} // return void
	}
}

// todo
func (loader *ClassLoader) resolveSuperClass(class *Class) {
	if class.superClassName != "" {
		class.SuperClass = loader.LoadClass(class.superClassName)
	}
}
func (loader *ClassLoader) resolveInterfaces(class *Class) {
	interfaceCount := len(class.interfaceNames)
	if interfaceCount > 0 {
		class.Interfaces = make([]*Class, interfaceCount)
		for i, interfaceName := range class.interfaceNames {
			class.Interfaces[i] = loader.LoadClass(interfaceName)
		}
	}
}

func calcStaticFieldSlotIds(class *Class) {
	slotId := uint(0)
	for _, field := range class.Fields {
		if field.IsStatic() {
			field.SlotId = slotId
			slotId++
		}
	}
	class.staticFieldCount = slotId
}

func calcInstanceFieldSlotIds(class *Class) {
	slotId := uint(0)
	if class.superClassName != "" {
		slotId = class.SuperClass.instanceFieldCount
	}
	for _, field := range class.Fields {
		if !field.IsStatic() {
			field.SlotId = slotId
			slotId++
		}
	}
	class.instanceFieldCount = slotId
}

func prepare(class *Class) {
	class.StaticFieldSlots = make([]Slot, class.staticFieldCount)
	for _, field := range class.Fields {
		if field.IsStatic() {
			class.StaticFieldSlots[field.SlotId] = EmptySlot // TODO
		}
	}
}

// todo
func (loader *ClassLoader) DefineClass(name string, data []byte) *Class {
	return loader.loadClass(name, data)
}
